﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.ServiceModel;
using System.ServiceProcess;

namespace RouterControl.Whs.SingletonService
{

    public sealed class SingletonService2Factory : IDisposable
    {
        public enum ServiceStatus
        {
            NotInstalled,
            Running,
            Stopped,
        }

        public const string WindowsServiceName = "RouterControl";

        private readonly log4net.ILog _log = log4net.LogManager.GetLogger(typeof(SingletonService2Factory));
        private ServiceController _serviceController = new ServiceController(WindowsServiceName);
        private ChannelFactory<ISingletonService2> _factory;

        public ISingletonService2 Channel
        {
            get { return _factory.CreateChannel(); }
        }

        public Uri Endpoint
        {
            get { return _factory.Endpoint.Address.Uri; }
        }

        public CommunicationState State
        {
            get { return _factory.State; }
        }

        public ServiceStatus IsWindowsServiceRunning()
        {
            try
            {
                _serviceController.Refresh();

                return (_serviceController.Status == ServiceControllerStatus.Running ? ServiceStatus.Running : ServiceStatus.Stopped);
            }
            catch (InvalidOperationException ex)
            {
                // service not installed
                // e.g. when the addin was just uninstalled
                _log.Warn("Router Control Service not installed?", ex);

                return ServiceStatus.NotInstalled;
            }
            catch (Exception ex)
            {
                _log.Error("Failed to determine Router Control Service status", ex);

                throw;
            }
        }

        public void StartWindowsService()
        {
            if (_log.IsDebugEnabled)
                _log.Debug("Starting Router Control service...");

            try
            {
                _serviceController.Start();

                // wait for the service being started
                _serviceController.WaitForStatus(ServiceControllerStatus.StartPending);

                var timeoutTime = DateTime.Now.Add(Properties.Settings.Default.SingeltonServiceStartStopTimeout);

                for (;;)
                {
                    _serviceController.Refresh();

                    if (_serviceController.Status == ServiceControllerStatus.Running)
                        break;

                    if (_serviceController.Status == ServiceControllerStatus.Stopped)
                        throw new Exception("Service did stop on start. See application event log for startup errors.");

                    if (DateTime.Now >= timeoutTime)
                    {
                        throw new Exception(
                            "Router Control service did not start within " +
                            Properties.Settings.Default.SingeltonServiceStartStopTimeout
                            );
                    }
                }

                _log.Info("Started Router Control service");
            }
            catch (Exception ex)
            {
                _log.Error("Failed to start Router Control Service", ex);

                throw;
            }
        }

        public void StopWindowsService()
        {
            if (_log.IsDebugEnabled)
                _log.Debug("Stopping Router Control service...");

            try
            {
                _serviceController.Stop();

                _serviceController.WaitForStatus(
                    ServiceControllerStatus.Stopped,
                    Properties.Settings.Default.SingeltonServiceStartStopTimeout
                    );

                _log.Info("Stopped Router Control service");
            }
            catch (Exception ex)
            {
                _log.Error("Failed to stop Router Control Service", ex);

                throw;
            }
        }

        public SingletonService2Factory()
        {
            _factory = new ChannelFactory<ISingletonService2>(
                new NetTcpBinding()
                {
                    MaxReceivedMessageSize = (1024 * 1024), // 1MB
                    MaxBufferPoolSize = (8 * 1024 * 1024), // 8MB
                    OpenTimeout = new TimeSpan(0, 0, 10),   // 1 minute
                    CloseTimeout = new TimeSpan(0, 0, 10),  // 1 minute
                    ReceiveTimeout = new TimeSpan(0, 10, 0),// 10 minutes
                    SendTimeout = new TimeSpan(0, 10, 0),   // 10 minutes
                    TransferMode = TransferMode.Streamed,
                },
                new EndpointAddress(new Uri(Properties.Settings.Default.SingletonService2Endpoint))
                );

            _factory.Faulted += new EventHandler(factory_Faulted);

            _factory.Open();
        }

        private void factory_Faulted(object sender, EventArgs e)
        {
            _log.Warn("WCF client faulted");
        }

        public void Dispose()
        {
            if (_factory != null)
            {
                _factory.Close();
            }
        }

    }
}
